﻿namespace iFinance.Infrastructure.ViewModels
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq.Expressions;
    using Microsoft.Practices.Prism.ViewModel;

    /// <summary>
    /// 带数据验证逻辑的ViewModelBase
    /// </summary>
    public class ValidationNotificationObject : NotificationObject, INotifyDataErrorInfo
    {
        #region Method

        /// <summary>
        /// 根据表达式获取属性名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        protected string GetPropertyName<T>(Expression<Func<T>> propertyName)
        {
            var memberExpression = propertyName.Body as MemberExpression;
            if (memberExpression != null)
            {
                return memberExpression.Member.Name;
            }
            return string.Empty;
        }

        /// <summary>
        /// 验证属性的值是否正确
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="validation">验证规则</param>
        /// <returns></returns>
        protected bool Valid(string propertyName, string errorMsg, Func<bool> validation)
        {
            bool isValid = validation();
            if (!isValid)
            {
                AddError(propertyName, errorMsg);
            }
            else
            {
                RemoveError(propertyName, errorMsg);
            }
            return isValid;
        }

        /// <summary>
        /// 验证属性的值是否正确
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="propertyName">属性名</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="validation">验证规则</param>
        /// <returns></returns>
        protected bool Valid<T>(Expression<Func<T>> propertyName, string errorMsg, Func<bool> validation)
        {
            return Valid(GetPropertyName(propertyName), errorMsg, validation);
        }

        /// <summary>
        /// 验证必填类型
        /// </summary>        
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <returns>验证是否通过</returns>
        protected bool ValidRequire(string propertyName, object value, string errorMsg)
        {
            return Valid(propertyName, errorMsg, () => { return value != null; });
        }

        /// <summary>
        /// 验证必填类型
        /// </summary>        
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <returns>验证是否通过</returns>
        protected bool ValidRequire<T>(Expression<Func<T>> propertyName, object value, string errorMsg)
        {
            return ValidRequire(GetPropertyName(propertyName), value, errorMsg);
        }

        /// <summary>
        /// 验证范围
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="minValue">最小值（包含）</param>
        /// <param name="maxValue">最大值（包含）</param>
        /// <returns></returns>
        protected bool ValidRange<T>(string propertyName, T value, string errorMsg, T minValue, T maxValue)
            where T : IComparable<T>
        {
            return Valid(propertyName, errorMsg,
                () =>
                {
                    return value.CompareTo(maxValue) <= 0 && value.CompareTo(minValue) >= 0;
                });
        }

        /// <summary>
        /// 验证范围
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="minValue">最小值（包含）</param>
        /// <param name="maxValue">最大值（包含）</param>
        /// <returns></returns>
        protected bool ValidRange<T>(Expression<Func<T>> propertyName, T value, string errorMsg, T minValue, T maxValue)
            where T : IComparable<T>
        {
            return ValidRange(GetPropertyName(propertyName), value, errorMsg, minValue, maxValue);
        }

        /// <summary>
        /// 验证字符串长度
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="minLength">最小长度（包含）</param>
        /// <param name="maxLength">最大长度（包含）</param>
        /// <returns>验证是否通过</returns>
        protected bool ValidStringLength(string propertyName, string value, string errorMsg, int minLength, int maxLength, bool trim = true)
        {
            return Valid(propertyName, errorMsg,
                () =>
                {
                    int len = string.IsNullOrEmpty(value) ? 0 : trim ? value.Trim().Length : value.Length;
                    return len <= maxLength && len >= minLength;
                });
        }

        /// <summary>
        /// 验证字符串长度
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="minLength">最小长度（包含）</param>
        /// <param name="maxLength">最大长度（包含）</param>
        /// <returns>验证是否通过</returns>
        protected bool ValidStringLength<T>(Expression<Func<T>> propertyName, string value, string errorMsg, int minLength, int maxLength, bool trim = true)
        {
            return ValidStringLength(GetPropertyName(propertyName), value, errorMsg, minLength, maxLength, trim);
        }

        /// <summary>
        /// 验证字符串不能为空
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="trim"></param>
        /// <returns></returns>
        protected bool ValidStringNotEmpty(string propertyName, string value, string errorMsg, bool trim = true)
        {
            return ValidStringLength(propertyName, value, errorMsg, 1, int.MaxValue, trim);
        }

        /// <summary>
        /// 验证字符串不能为空
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">要验证的值</param>
        /// <param name="errorMsg">验证不通过时的错误信息</param>
        /// <param name="trim"></param>
        /// <returns></returns>
        protected bool ValidStringNotEmpty<T>(Expression<Func<T>> propertyName, string value, string errorMsg, bool trim = true)
        {
            return ValidStringLength(GetPropertyName(propertyName), value, errorMsg, 1, int.MaxValue, trim);
        }

        /// <summary>
        /// 添加一条错误信息
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="errorMsg">错误信息</param>
        public void AddError(string propertyName, string errorMsg)
        {
            if (!errorDict.ContainsKey(propertyName))
            {
                errorDict[propertyName] = new List<string>();
            }

            if (!errorDict[propertyName].Contains(errorMsg))
            {
                errorDict[propertyName].Add(errorMsg);
                RaiseErrorsChangedEvent(propertyName);
            }
        }

        /// <summary>
        /// 添加一条错误信息
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="errorMsg">错误信息</param>
        public void AddError<T>(Expression<Func<T>> propertyName, string errorMsg)
        {
            AddError(GetPropertyName(propertyName), errorMsg);
        }

        /// <summary>
        /// 删除一条错误信息
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="errorMsg">属性名</param>
        public void RemoveError(string propertyName, string errorMsg)
        {
            if (errorDict.ContainsKey(propertyName) && errorDict[propertyName].Contains(errorMsg))
            {
                errorDict[propertyName].Remove(errorMsg);

                if (errorDict[propertyName].Count == 0)
                    errorDict.Remove(propertyName);

                RaiseErrorsChangedEvent(propertyName);
            }
        }

        /// <summary>
        /// 删除一条错误信息
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <param name="errorMsg">属性名</param>
        public void RemoveError<T>(Expression<Func<T>> propertyName, string errorMsg)
        {
            RemoveError(GetPropertyName(propertyName), errorMsg);
        }

        #endregion

        #region INotifyDataErrorInfo

        /// <summary>
        /// 错误字典
        /// </summary>
        protected Dictionary<string, List<string>> errorDict = new Dictionary<string, List<string>>();

        /// <summary>
        /// 错误状态改变事件
        /// </summary>
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        /// <summary>
        /// 触发错误状态改变事件
        /// </summary>
        /// <param name="propertyName"></param>
        protected virtual void RaiseErrorsChangedEvent(string propertyName)
        {
            var temp = ErrorsChanged;
            if (temp != null)
            {
                temp(this, new DataErrorsChangedEventArgs(propertyName));
                RaisePropertyChanged(() => this.HasErrors);
            }
        }

        /// <summary>
        /// 获取错误
        /// </summary>
        /// <param name="propertyName">属性名</param>
        /// <returns>错误列表</returns>
        public virtual IEnumerable GetErrors(string propertyName)
        {
            if (errorDict.ContainsKey(propertyName))
            {
                return errorDict[propertyName];
            }
            return null;
        }

        /// <summary>
        /// 是否有错误
        /// </summary>
        public virtual bool HasErrors
        {
            get { return errorDict.Count > 0; }
        }

        #endregion
    }
}
